package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.aliyun.scan.GreenScan;
import com.heima.aliyun.scan.ScanResult;
import com.heima.common.constants.message.PublishArticleConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.config.DeclarePubArticleRabbitConfig;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {

   //依赖注入
    @Autowired
    private WmNewsService wmNewsService;

    //引入外部文件资源
    @Value("${file.oss.web-site}")
    String webSite;
    @Autowired
    private AdminFeign adminFeign;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void autoScanWmNews(Integer id) {
        //1.根据id查询自媒体文章信息
       log.info("文章自动审核方法触发    待审核的文章id : {}",id);
       //1.1参数校验
        if (id==null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"查询的文章id不存在");
        }
        WmNews wmNews = wmNewsService.getById(id);
        if (wmNews==null){
            log.error("文章不存在，文章自动审核失败   待审核的文章id : {}");
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"查询的文章不存在");
        }
        //2.判断文章的状态是否为1（防止重复消费）
        if (!WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(wmNews.getStatus())){
            log.error("审核文章的状态不是为待审核状态，自动审核失败 待审核文章id：{}",id);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"审核文章不是为待审核状态");
        }
        //3.抽取出文本文件的内容和所有的图片路径
       Map contentAndImages = parseContentImages(wmNews);
        //3.1开启DFA敏感词查询
       boolean sensitive= handleSensitive((String)contentAndImages.get("content"),wmNews);
       if (!sensitive){
           //检测未通过，修改文章状态为2，返回未通过信息
           //检测通过，继续进行下一步
           log.error("文章审核未通过，文章中含有敏感词，文章id:{}");
           return;
       }

        //3.2开启阿里云内容安全审核
       boolean handcontent= handleText((String)contentAndImages.get("content"),wmNews);
       if (!handcontent){
           //检测未通过，修改文章状态为2，返回未通过的信息
           //待人工审核，状态改为3
           //检测通过，进行进行下一步
           CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章审核未通过，文章内容中有敏感词信息 文章id: {}");
       }
        //3.3阿里云图片内容安全检测
        List<String> images = (List<String>) contentAndImages.get("images");
       //判断图片是否为空
        if (!CollectionUtils.isEmpty(images)){
          boolean imagesScan =  handleImages(images,wmNews);
          if (!imagesScan){
              //未通过，修改状态为2
              //通过，继续执行下一步
              log.error("阿里云图片检测不通过，图片含有违规信息，文章id:{}",id);
              return;
          }
        }

        //4.如果文章全部状态审核通过，将状态修改为8
        updateWmNews(wmNews,WmNews.Status.SUCCESS.getCode(), "审核通过");
        // 5. TODO 根据文章的具体发布时间   发送延时发布消息
        //5.1计算延迟时间
        //发布时间
        long pushTime = wmNews.getPublishTime().getTime();
        //当前时间
        long noeTime = System.currentTimeMillis();
        //延迟时间
        long lateTime = pushTime-noeTime;
        //5.2发送消息并设置延迟时间
        rabbitTemplate.convertAndSend(
                //交换机
                PublishArticleConstants.DELAY_DIRECT_EXCHANGE,
                //队列
                PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY,
                //发送的内容
                wmNews.getId(),

         new MessagePostProcessor() {
             /**
              * 消息的后置处理器，可以对消息属性进行设置
              * @param message
              * @return
              * @throws AmqpException
              */
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置 x-delay 延迟时间
                //延迟时间小于等于0直接发送
                message.getMessageProperties().setHeader("x-delay",lateTime<=0?0:lateTime);
                return message;
            }
        }
        );
        log.info("成功发送延时发布文章消息   文章id:{}  延迟时间: {}",wmNews.getId(),lateTime);
    }

    /**
     * 阿里云图片安全检测
     * @param images
     * @param wmNews
     * @return
     */
    private boolean handleImages(List<String> images, WmNews wmNews) {
        boolean flag=false;
        //1.调用图片检测的工具类
        try {
            ScanResult imageUrlScan = greenScan.imageUrlScan(images);
            //2.  判断suggestion   block 2  review 3  pass 返回true    null  3
            String suggestion = imageUrlScan.getSuggestion();
            switch (suggestion){
                case "block":
                    updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"图片审核不同，里面含有敏感信息"+imageUrlScan.getLabel());
                break;
                case "review":
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"阿里云图片审核出现不确定情况，转为人工审核");
                    break;
                case "pass":
                    flag=true;
                    break;
                default:
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"阿里云图片审核检测出现异常，转为人工审核");
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            //3   根据 suggestion的值修改文章状态
            // 如果阿里云调用异常，将状态修改为3  打印错误日志信息
            log.error("阿里云 文本审核出现异常， 当前文章转为人工审核状态");
            updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"阿里云图片审核检测出现异常，转为人工审核");
        }
        return flag;
    }

    /**
     * 阿里云文本内容检测
     * @param content
     * @param wmNews
     * @return
     */
    @Autowired
    private GreenScan greenScan;

    private boolean handleText(String content, WmNews wmNews) {
        boolean flag=false;
    //1.使用阿里云检测文本内容
        try {
            ScanResult scanResult = greenScan.greenTextScan(content);
            //2.  判断suggestion   block 2  review 3  pass 返回true    null  3
            String suggestion = scanResult.getSuggestion();
            switch (suggestion){
                case "block":
                        updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"文本内容中含有敏感词"+scanResult.getLabel());
                break;
                case "review":
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"文本内容含有不确定信息，转为人工审核");
                break;
                case "pass":
                    flag=true;
                    break;
                default:
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"阿里云文本检测出现异常，转为人工审核");
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            //3   根据 suggestion的值修改文章状态
            // 如果阿里云调用异常，将状态修改为3  打印错误日志信息
            log.error("阿里云 文本审核出现异常， 当前文章转为人工审核状态");
            updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"阿里云文本检测出现异常，转为人工审核");
        }
        return flag;
    }


    /**
     * 开启DAF敏感词查询
     * @param content
     * @param wmNews
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        // 1. 使用feign远程查询敏感词列表
        ResponseResult<List<String>> sensitivesResult = adminFeign.sensitives();
        if (!sensitivesResult.checkCode()) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,"查询敏感词列表，远程调用失败");
        }
        List<String> sensitives = sensitivesResult.getData();
        // TODO 先从redis中 查询是否有敏感词列表, 如果redis中没有 远程调用数据库查询
        // TODO 如果有直接获取redis中的缓存
        // 2. 基于DFA算法 封装数据模型
        SensitiveWordUtil.initMap(sensitives);
        // 3. 基于DFA算法，检测文本内容中是否包含敏感词
        Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
        if (!CollectionUtils.isEmpty(map)){
            // 有敏感词   将文章的状态修改为2  并设置失败原因
            updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"文本内容中包含敏感词 "+map );
            return false;
        }
        return true;
    }

    /**
     * 修改文章状态
     * @param wmNews
     * @param status
     * @param reason
     */
    private void updateWmNews(WmNews wmNews, short status, String reason) {
        wmNews.setStatus(status);
        wmNews.setReason(reason);
       wmNewsService.updateById(wmNews);
    }

    /**
     * 抽取出文章的文本内容和图片路径
     * @param wmNews
     * @return
     */
    private Map parseContentImages(WmNews wmNews) {
//        List<Map> mapList = JSON.parseArray(wmNews.getContent(), Map.class);
//        //1.抽取文章中的所有内容
//        // 家乡好美_hmtt_国家伟大,之间用_hmtt_相连接，防止出现审核出现敏感词
//        //获取流
//        String content = mapList.stream()
//                //过滤文本类型
//                .filter(m -> "text".equals(m.get("type")))
//                //获取value中的值
//                .map(m -> String.valueOf(m.get("value")))
//                .collect(Collectors.joining("_hmtt_"));
//        //文章标题  +  文章内容中的文本部分
//        content += "_hmtt_" + wmNews.getTitle();
//        //2.抽取文章图片所有路径
//        //获取流
//        List<String> images = mapList.stream()
//                .filter(m -> "image".equals(m.get("type")))
//                .map(m -> String.valueOf(m.get("value")))
//                .collect(Collectors.toList());
//        //获取文章封面图片的路径
//        String newsImages = wmNews.getImages();
//        //判断文章标题图片是否为空
//        if (StringUtils.isNotBlank(newsImages)) {
//            //1.按照逗号切割得到数组
//            //2.使用数组获取流对象
//            //3.使用map映射给每个图片路径添加前缀
//            //4.将所有图片放到一个集合中
//            List<String> conerImages = Arrays.stream(newsImages.split(","))
//                    .map(url -> webSite + url)
//                    .collect(Collectors.toList());
//            //将内容图片路径 和 封面图片路径合到一起
//            images.addAll(conerImages);
//        }
//            //去除重复图片
//            images = images.stream()
//                    .distinct()
//                    .collect(Collectors.toList());
//
//            Map map = new HashMap();
//            map.put("context", content);
//            map.put("images", images);
//            return map;
        List<Map> contentMapList = JSON.parseArray(wmNews.getContent(), Map.class);
        // 1. 抽取文章的所有文本内容
        // 家乡好美_hmtt_国家伟大
        String content = contentMapList.stream()
                // [{type:text,value:文本内容},{type:image,value:图片},{type:text,value:文本内容}]
                .filter(m -> "text".equals(m.get("type")))
                // [{type:text,value:文本内容},{type:text,value:文本内容}]
                .map(m -> String.valueOf(m.get("value")))
                // [文本内容,文本内容]
                .collect(Collectors.joining("_hmtt_"));
        //文本内容_hmtt_文本内容_hmtt_文章标题

        //    文章标题  +  文章内容中的文本部分
        content += "_hmtt_" + wmNews.getTitle();
        // 2. 抽取文章的所有图片路径
        List<String> images = contentMapList.stream()
                // [{type:text,value:文本内容},{type:image,value:图片},{type:text,value:文本内容}]
                .filter(m -> "image".equals(m.get("type")))
                // [{type:image,value:图片}]
                .map(m -> String.valueOf(m.get("value")))
                // [图片路径]
                .collect(Collectors.toList());
        //    文章封面图片路径 + 文章内容中的图片路径
        String coverStr = wmNews.getImages();
        if (StringUtils.isNotBlank(coverStr)) {
            // material/2022/7/20220728/3787d5ef88ad4e8cbbbb30ddeb35b1e80001.jpg
            // webSite + 图片相对路径.jpg    ,    图片相对路径.jpg,     图片相对路径.jpg
            // 1. 按照逗号切割  得到数组
            // 2. 基于数组 获取流对象
            // 3. 使用map映射方法，将每个路径添加访问前缀
            // 4. 收集到一个集合中
            List<String> coverImages = Arrays.stream(coverStr.split(","))
                    .map(url -> webSite + url)
                    .collect(Collectors.toList());
            // 将内容图片路径 和 封面图片路径合到一起
            images.addAll(coverImages);
        }
        // 去除重复图片
        images = images.stream()
                .distinct()
                .collect(Collectors.toList());

        Map result = new HashMap();
        result.put("content",content);
        result.put("images",images);
        return result;
        }
}
